using System.Diagnostics;
using FluentParser.Utils;

namespace FluentParser.Expressions
{
    /// <summary>
    /// When an expression parses a text, this object is returned.
    /// It contains the resulting value and other useful informations.
    /// </summary>
    /// <typeparam name="TValue">Type of the resulting value</typeparam>
    [DebuggerDisplay("[ParsingResult Index={Index} Length={Length} Value={Value}]")]
    public class ParsingResult<TValue>
    {
        public ParsingResult(int index, int length, TValue value)
        {
            Index = index;
            Length = length;
            Value = value;
        }

        /// <summary> Index of the first character parsed. </summary>
        public int Index { get; private set; }

        /// <summary> Length of the text used in the resulting value. </summary>
        public int Length { get; private set; }

        /// <summary> Index of the last character used in the resulting value. </summary>
        public int End { get { return Index + Length; } }

        /// <summary> The resulting value. </summary>
        public TValue Value { get; private set; }
    }

    public static class ParsingResults
    {

        // Build initial ParsingResult
        public static ParsingResult<T> Build<T>(int index, int length, T value)
        {
            return new ParsingResult<T>(index, length, value);
        }

        public static ParsingResult<Tuple<T1, T2>> Build<T1, T2>(int index, int length, T1 value1, T2 value2)
        {
            return new ParsingResult<Tuple<T1, T2>>(index, length, new Tuple<T1, T2>(value1, value2));
        }

        public static ParsingResult<Tuple<T1, T2, T3>> Build<T1, T2, T3>(int index, int length, T1 value1, T2 value2, T3 value3)
        {
            return new ParsingResult<Tuple<T1, T2, T3>>(index, length, new Tuple<T1, T2, T3>(value1, value2, value3));
        }

        public static ParsingResult<Tuple<T1, T2, T3, T4>> Build<T1, T2, T3, T4>(int index, int length, T1 value1, T2 value2, T3 value3, T4 value4)
        {
            return new ParsingResult<Tuple<T1, T2, T3, T4>>(index, length, new Tuple<T1, T2, T3, T4>(value1, value2, value3, value4));
        }


        // Combine normal ParsingResult
        public static ParsingResult<Tuple<T1, T2>> Join<T1, T2>(ParsingResult<T1> value1, ParsingResult<T2> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, value1.Value, value2.Value);
        }

        public static ParsingResult<Tuple<T1, T2, T3>> Join<T1, T2, T3>(ParsingResult<T1> value1, ParsingResult<T2> value2, ParsingResult<T3> value3)
        {
            return Build(value1.Index, value1.Length + value2.Length + value3.Length, value1.Value, value2.Value, value3.Value);
        }

        public static ParsingResult<Tuple<T1, T2, T3, T4>> Join<T1, T2, T3, T4>(ParsingResult<T1> value1, ParsingResult<T2> value2, ParsingResult<T3> value3, ParsingResult<T4> value4)
        {
            return Build(value1.Index, value1.Length + value2.Length + value3.Length, value1.Value, value2.Value, value3.Value, value4.Value);
        }


        // Combine ParsingResult with 2 values tuples
        public static ParsingResult<Tuple<T1, T21, T22>> Join<T1, T21, T22>(ParsingResult<T1> value1, ParsingResult<Tuple<T21, T22>> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, Tuple.Combine(value1.Value, value2.Value));
        }

        public static ParsingResult<Tuple<T11, T12, T2>> Join<T11, T12, T2>(ParsingResult<Tuple<T11, T12>> value1, ParsingResult<T2> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, Tuple.Combine(value1.Value, value2.Value));
        }

        public static ParsingResult<Tuple<T1, T2, T31, T32>> Join<T1, T2, T31, T32>(ParsingResult<T1> value1, ParsingResult<T2> value2, ParsingResult<Tuple<T31, T32>> value3)
        {
            return Build(value1.Index, value1.Length + value2.Length + value3.Length, Tuple.Combine(value1.Value, value2.Value, value3.Value));
        }

        public static ParsingResult<Tuple<T1, T21, T22, T3>> Join<T1, T21, T22, T3>(ParsingResult<T1> value1, ParsingResult<Tuple<T21, T22>> value2, ParsingResult<T3> value3)
        {
            return Build(value1.Index, value1.Length + value2.Length + value3.Length, Tuple.Combine(value1.Value, value2.Value, value3.Value));
        }

        public static ParsingResult<Tuple<T11, T12, T2, T3>> Join<T11, T12, T2, T3>(ParsingResult<Tuple<T11, T12>> value1, ParsingResult<T2> value2, ParsingResult<T3> value3)
        {
            return Build(value1.Index, value1.Length + value2.Length + value3.Length, Tuple.Combine(value1.Value, value2.Value, value3.Value));
        }

        public static ParsingResult<Tuple<T11, T12, T21, T22>> Join<T11, T12, T21, T22>(ParsingResult<Tuple<T11, T12>> value1, ParsingResult<Tuple<T21, T22>> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, Tuple.Combine(value1.Value, value2.Value));
        }


        // Combine ParsingResult with 3 values tuples
        public static ParsingResult<Tuple<T1, T21, T22, T23>> Join<T1, T21, T22, T23>(ParsingResult<T1> value1, ParsingResult<Tuple<T21, T22, T23>> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, Tuple.Combine(value1.Value, value2.Value));
        }

        public static ParsingResult<Tuple<T11, T12, T13, T2>> Join<T11, T12, T13, T2>(ParsingResult<Tuple<T11, T12, T13>> value1, ParsingResult<T2> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, Tuple.Combine(value1.Value, value2.Value));
        }


        // Special joins
        public static ParsingResult<T1> Join<T1>(ParsingResult<T1> value1, ParsingResult<string> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, value1.Value);
        }

        public static ParsingResult<T2> Join<T2>(ParsingResult<string> value1, ParsingResult<T2> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, value2.Value);
        }

        public static ParsingResult<string> Join(ParsingResult<string> value1, ParsingResult<string> value2)
        {
            return Build(value1.Index, value1.Length + value2.Length, value1.Value + value2.Value);
        }


    }
}